﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using Utils;

namespace Model
{
    [Serializable]
    public class Surface
    {
        public List<Wall> Walls;

        public Surface()
        {
            Walls = new List<Wall>();
        }

        public void DrawSurface()
        {
            

            foreach (var wall in Walls)
            {
                wall.DrawWall();
            }

            
        }
    }

    [Serializable]
    public class Wall
    {
        public bool Complex;
        public Color Color;
        public List<Point3D> Points;
        public Point3D Normal;
        [NonSerialized]
        private BoundingBox _box;
        public BoundingBox Box
        {
            get
            {
                if (_box == null)
                {
                    double maxX = double.MinValue;
                    double minX = double.MaxValue;

                    double maxY = double.MinValue;
                    double minY = double.MaxValue;

                    double maxZ = double.MinValue;
                    double minZ = double.MaxValue;

                    foreach (var point3D in Points)
                    {
                        if (maxX < point3D.X)
                            maxX = point3D.X;

                        if (minX > point3D.X)
                            minX = point3D.X;

                        if (maxY < point3D.Y)
                            maxY = point3D.Y;

                        if (minY > point3D.Y)
                            minY = point3D.Y;

                        if (maxZ < point3D.Z)
                            maxZ = point3D.Z;

                        if (minZ > point3D.Z)
                            minZ = point3D.Z;
                    }

                    double width = (maxX - minX);
                    double height = (maxY - minY);
                    double depth = (maxZ - minZ);

                    var center = new Point3D(maxX - width / 2, maxY - height / 2, maxZ - depth / 2);

                    if (Complex)
                        Box = new BoundingBox(Points, center, height, width, depth);
                    else
                        Box = new BoundingBox(center, height, width, depth);
                }
                return _box;
            }
            private set { _box = value; }
        }

        public Wall()
        {
            Points = new List<Point3D>();
        }

        public Wall(Color color)
            : this()
        {
            Color = color;
        }

        public void DrawWall()
        {
            if (Color != Color.Empty)
                GL.Color3(Color);

            if (Normal == null)
            {
                var v12 = new Vector3d(Points[1].X - Points[0].X, Points[1].Y - Points[0].Y, Points[1].Z - Points[0].Z);
                var v14 = new Vector3d(Points[3].X - Points[0].X, Points[3].Y - Points[0].Y, Points[3].Z - Points[0].Z);

                var cross = Vector3d.Cross(v12, v14);
                cross = Vector3d.Normalize(cross);

                Normal = new Point3D(cross.X, cross.Y, cross.Z);

            }
            var n = new Vector3d(Normal.X, Normal.Y, Normal.Z);
            GL.Normal3(n);
            if (Texture != -1)
            {
                GL.Enable(EnableCap.Texture2D);
                GL.BindTexture(TextureTarget.Texture2D, Texture);
            }
            GL.Begin(BeginMode.Quads);
            for (int i = 0; i < Points.Count; i++)
            {
                //GL.Normal3(point.X, point.Y, point.Z);
                if (Texture != -1)
                {
                    switch (i % 4)
                    {
                        case 0:
                            {
                                GL.TexCoord2(0, 0);
                                break;
                            }
                        case 1:
                            {
                                GL.TexCoord2(1, 0);
                                break;
                            }
                        case 2:
                            {
                                GL.TexCoord2(1, 1);
                                break;
                            }
                        case 3:
                            {
                                GL.TexCoord2(0, 1);
                                break;
                            }
                    }
                }
                GL.Vertex3(Points[i].X, Points[i].Y, Points[i].Z);
            }
            GL.End();
        }

        public int Texture { get; set; }
    }

    [Serializable]
    public class Ramp : Wall
    {
        public Ramp(Color color, Point3D start, Point3D end, double width)
            : this(start, end, width)
        {
            Color = color;
        }
        private static bool oneNotTwo;
        public Ramp(Point3D start, Point3D end, double width)
        {
            Texture = oneNotTwo ? TextureUtils.RAMP : TextureUtils.RAMP2; ;
            oneNotTwo = !oneNotTwo;
            bool xSlope = start.X != end.X;

            Complex = true;
            if (xSlope)
            {
                if (start.X < end.X)
                {
                    var temp = start;
                    start = end;
                    end = temp;
                }

                var p1 = new Point3D(start.X, start.Y, start.Z + (width / 2));
                var p2 = new Point3D(start.X, start.Y, start.Z - (width / 2));

                var p3 = new Point3D(end.X, end.Y, end.Z - (width / 2));
                var p4 = new Point3D(end.X, end.Y, end.Z + (width / 2));
                Points = new List<Point3D>(new[] { p1, p2, p3, p4 });
            }
            else
            {
                if (start.Z < end.Z)
                {
                    var temp = start;
                    start = end;
                    end = temp;
                }
                var p1 = new Point3D(start.X - (width / 2), start.Y, start.Z);
                var p2 = new Point3D(start.X + (width / 2), start.Y, start.Z);

                var p3 = new Point3D(end.X + (width / 2), end.Y, end.Z);
                var p4 = new Point3D(end.X - (width / 2), end.Y, end.Z);
                Points = new List<Point3D>(new[] { p1, p2, p3, p4 });
            }
        }
    }
}
